package com.hexb.core.scanner

import com.hexb.core.mybatis.entry.MapperEntry
import org.apache.log4j.Logger
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.core.io.support.ResourcePatternResolver
import org.springframework.core.type.classreading.CachingMetadataReaderFactory
import org.springframework.core.type.classreading.MetadataReaderFactory
import org.springframework.util.ClassUtils
import org.springframework.util.StringUtils
import org.springframework.util.SystemPropertyUtils

import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
 * @Package : com.hexb.mt.config
 * @Author : hexb 
 * @Date : 2018-08-11 09:48
 */
class BaseMapperScanner {

    static private final String MYBATIS_MAPPER_ANNOTATION = 'org.apache.ibatis.annotations.Mapper'
    private String baseMapperClassName = 'com.hexb.core.mapper.BaseMapper'
    static private Logger logger = Logger.getLogger(BaseMapperScanner.class)


    def scanner(String scanPackages) {
        String[] packages = scanPackages ? scanPackages?.split(',') : ['']
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver()
        List<MapperEntry> list = []
        packages.findAll { !StringUtils.isEmpty(it) }.each {
            Resource[] resources = resourcePatternResolver.getResources(scanPath(it))
            try {
                list += resources.collect {
                    readMapperDefine(it)
                }.findAll { it }
            } catch (e) {
                logger.error(e)
            }
        }
        list
    }

    def readMapperDefine(Resource resource) {
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory()
        def reader = metadataReaderFactory.getMetadataReader(resource)
        def annotations = reader.annotationMetadata

        MapperEntry bmt = null

        if (annotations.hasAnnotation(MYBATIS_MAPPER_ANNOTATION)) {
            def classMetadata = reader.classMetadata
            if (classMetadata.isInterface() && classMetadata.interfaceNames.contains(baseMapperClassName)) {
                bmt = new MapperEntry(mapperName: classMetadata.className)
                Class<?> clazz = Class.forName(classMetadata.className)
                clazz.getTypeName()
                try {
                    def interfaces = clazz.getGenericInterfaces()
                    interfaces.each { type ->
                        Type[] types = ((ParameterizedType) type).getActualTypeArguments()

                        bmt.pkType = types[0].typeName
                        bmt.entryType = types[1].typeName
                    }
                } catch (e) {
                    logger.error(e)
                }
            }
        }
        bmt
    }

    static def scanPath(String basePackage) {
        String prefix = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
        String packagePath = ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage))
        "$prefix$packagePath/**/*.class"
    }
}
